import inspect
import subprocess
import os,json,sys
import time,platform
import threading
from urllib.parse import unquote
from urllib.parse import quote
import socket
import gevent
import gevent.server
import paho.mqtt.client as mqtt
class base_class:
    base_import_lst=[
        "import inspect",
        "import subprocess",
        "import os,json,sys",
        "import time,platform",
        "import threading" ,
        "from urllib.parse import unquote",
        "from urllib.parse import quote",
        "import socket",
        "import gevent",
        "import gevent.server",
        ]
    @classmethod
    def write_pid(self,pid):
        self.pid_dir=r'E:\my\pid_dir'
        with open(self.pid_dir+os.sep+self.__class__.__name__ ,'w',encoding='utf-8')as f:
            f.write(str(pid))
    _process_dct={}#如果组件另开进程，需要在另开的进程内创建
    _all_obj={}
        #编码
    @classmethod
    def _encode_url(cls,data):
        """
        将含有特殊字符的字符串进行 URL 编码。
        
        :param data: 字符串
        :return: 编码后的字符串
        """
        encoded_data = quote(data, safe='')
        return encoded_data
    #解码
    @classmethod
    def _decode_url(cls,encoded_data):
        """
        将 URL 编码后的字符串解码。
        
        :param encoded_data: 编码后的字符串
        :return: 解码后的字符串
        """
        decoded_data = unquote(encoded_data)
        return decoded_data
    
    @classmethod
    def _encode2(cls,data):
        return cls._encode_url(json.dumps(data))
    @classmethod
    def _decode2(cls,data):
        return json.loads(cls._decode_url(data)) 
    def __init__(self,**kwargs):
        sys.stdout.reconfigure(encoding='utf-8')
        sys.stderr.reconfigure(encoding='utf-8')
        self.kwargs=kwargs
        self.project_dir_path=kwargs.get("project_dir_path")#项目所在目录
        self.recv_port=kwargs.get("recv_port")#当前进程通信的port
        self.process_id=kwargs.get("process_id")#当前进程的id
        self.flow_id=kwargs.get("flow_id","None")#流程id
        self.temp_id=kwargs.get("temp_id","None")#流程中被拖拽的组件id
        self.temp_name=kwargs.get("temp_name")#流程中的组件名称
        self.db_temp_id=kwargs.get("db_temp_id")#模板组件id
        self.db_temp_level=kwargs.get("db_temp_level")#组件版本号
        if self.process_id is not None:
            self._process_dct[self.process_id].create_
        self.init(**kwargs)
        self.in_params=kwargs.get("in_params")
        self.out_params=kwargs.get("out_params")
        self._name=kwargs.get("name")
        self._all_obj[self._name]=self
    @classmethod
    def create_class(cls,python_str):
        exec(python_str)
    @classmethod
    def delete_class(cls,class_name):
        exec("del "+class_name)

    #创建当前进程与其他进程通信 的收发通道
    def createsend_message_and_recv(self):
        th2 = threading.Thread(target=self._recv, args=())
        th2.setDaemon(True)
        th2.start()
        th2.join(0)
    #将next后需要执行的数据 传入进程中
    def send_next_data(self,**kwargs):
        pass
    #非相同进程时 send_next替代run_next
    def send_next(self,**kwargs):
        pass
    #检查传参是否全部传递成功
    def run_next(self,**kwargs):
        for key in kwargs:
            if key in self.in_params and self.in_params[key]=="_&":
                self.in_params[key]=kwargs[key]
        #检查参数是否全部传递成功  也即in_params里面的value全为True
        if all(value != "_&" for value in self.in_params.values()):
            #传递成功，执行run函数
            self.run_mid(self.in_params)
    def run_mid(self,kwargs):
        print(self._name,"开始运行")
        t=time.time()
        out_params=self.run(**kwargs)
        self.in_params={k:'_&'for k in self.in_params }
        print(self._name,"结束运行",'%.2f'%(time.time()-t))
        for key,value in self.out_params.items():
            self._all_obj[value['out_obj']].run_next(**{v:out_params[k]for k,v in value['out_params'].items()})
    def init(self,**kwargs):
        pass
    def run(self,**kwargs):
        return kwargs
    def _getsource(self):
        return inspect.getsource(self.__class__)
    def _delete_process(self):
        # 终止进程
        os.kill(self.process_id, 9)  # 9 表示SIGKILL信号，强制结束进程
    def handle(self,gev_obj,socket,address):
        while True:
            data = socket.recv(1024)
            if not data:
                break
            socket.sendall(data)
    def _recv(self):
        #开启单线程tcp服务器 端口为self.recv_port 处理函数为self.handle 
        ##发送10w条数据(0-10w) 可以达到10000/s  在windows 32G内存 2G处理器 
        class new_class(gevent.server.StreamServer):
            def handle(gev,socket,address):
                self.handle(gev,socket,address)
        gevent_obj=new_class(('localhost',self.recv_port))
        gevent_obj.serve_forever()
            
    def send_message(self,server_ip="127.0.0.1",port=5555,message="test"):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
                sock.settimeout(0.5)  # 设置超时时间为500ms
                sock.connect((server_ip, port))
                message = message.encode('utf-8')
                sock.sendall(message)
                response = sock.recv(1024)
                return response
        except:
            return False
    def create_bat_or_shell(self):
        run_process_dir=os.path.join(self.project_dir_path,"run_process",self.flow_id+'_'+self.temp_id,self.temp_name)+os.sep
        try:os.makedirs(run_process_dir)
        except:pass
        
        self.py_file_path=run_process_dir+"server.py"
        self.log_file_path=run_process_dir+"server.log"
        if self.check_windows()is True:
            self.bat_file_path=run_process_dir+"server.bat"
            with open(self.bat_file_path,'w',encoding='utf-8')as f:
                f.write('\n'.join(['@echo off',
                                    'if "%1" == "h" goto begin',
                                    'mshta vbscript:createobject("wscript.shell").run("""%~0"" h",0)(window.close)&&exit',
                                    ':begin',
                                   'setlocal',
                                   ':: 设置日志文件的路径',
                                   f'set LOG_FILE={self.log_file_path}',
                                   ':: 执行Python脚本,并将输出和错误重定向到同一个日志文件  ',
                                   f'python -u {self.py_file_path} > "%LOG_FILE%" 2>&1 ',
                                   ":: 结束批处理文件",
                                   "endlocal",
                                   ]))
        else:pass
    def create_py_file(self):
        run_process_dir=os.path.join(self.project_dir_path,"run_process",self.flow_id+'_'+self.temp_id,self.temp_name)+os.sep
        try:os.makedirs(run_process_dir)
        except:pass
        self.py_file_path=run_process_dir+"server.py"
        self.import_lst=["import paho.mqtt.client as mqtt"]
        #创建py文件
        with open(self.py_file_path,'w',encoding="utf-8") as f:
            write_str="\n".join(self.base_import_lst)+"\n"+"\n".join(self.import_lst)+"\n"+base_class_obj._getsource()+"\n"+self._getsource()+"\n".join([
            'if __name__=="__main__":',
            f'    {self.__class__.__name__ }.write_pid(os.getpid())',
            f'    obj={self.__class__.__name__ }(**{self.kwargs})',
            '    obj.run_server()',
            ])
            f.write(write_str)

    def _create_process(self):
        self.bat_file_path=r'E:\my\easy_edit\_base_tool\python\run_process\None_None\mqtt_test\server3.bat'
        # 构建命令行参数
        subprocess.Popen(f'start "" {self.bat_file_path}', shell=True)
        print("???")
    def create_obj(self):
        print("#检查目标服务是否启动")
        if self.send_message(message="A") is False:
            print("开始创建  bat or shell")
            self.create_bat_or_shell()
            print("开始创建  py file")
            self.create_py_file()
            print("开始运行 server")
            self._create_process()
        print("创建进程结束")
        return self.recv_port

    def check_windows(self):
        # 判断操作系统
        if os.name == 'nt':
            print("当前环境是 Windows")
            return True
        elif os.name == 'posix':
            if os.uname().sysname == 'Linux':
                return False

class mqtt_class(base_class):
    def __on_connect(self,client, userdata, flags, rc):
        if rc == 0:
            print("Connected to MQTT broker")
            client.subscribe("topic1")
        else:
            print("Failed to connect, return code %d\n", rc)
    def __on_message(self,client, userdata, message):
        print(message.topic,{str(message.payload.decode('utf-8'))})

    def _add_topic(self):
        pass
    def init(self,**kwargs):
        self.broker_address=kwargs.get("broker_address")
        self.broker_port=kwargs.get("broker_port")
        self.fast_topics={}#topic快速搜索字典
        self.topic_info={}
        self.a=0
    def add_topic(self,topic,info):
        self.mqtt_client.subscribe(topic)#订阅
        new_topics=self.fast_topics
        for i in topic:
            new_topics2=new_topics.get(i)
            if new_topics2 is None:
                new_topics[i]={}
                new_topics=new_topics[i]
            else:
                new_topics=new_topics2
        new_topics['value']=info 
    #绑定topic
    def mqtt_handler(self,id_,topic, handle):  
        self.add_topic(topic,{"id":id_, "handle": handle})
        self.topic_info()
        #id 唯一id 如 (用户id,目录id,项目id,流程id,组件id) 
    #取消绑定
    def mqtt_unhandler(self,topic):
        pass
    def find_topic(self,topic):
        match_topic=''
        now_topic=self.fast_topics
        for i in topic:
            # print('find',i,topics)
            if '#'in now_topic:
                return {"match_topic":match_topic+'#',"info":now_topic['#']['value']}
            else:
                match_topic+=i
                now_topic=now_topic.get(i)
                if now_topic is None:return#没有匹配到
        if now_topic.get('value') is True:return {"match_topic":match_topic,"info":now_topic['value']}
        elif now_topic.get('#')is not None:return {"match_topic":match_topic+'#',"info":now_topic['#']['value']}
        else:return None#没有匹配到
    #添加topic以及相关响应处理

    #匹配topic的时间   100w条topic匹配一条topic的时间在0s左右
    def on_message(self, client, userdata, msg):  
        topic = msg.topic  
        payload = msg.payload.decode()  
        now_topic_info=self.find_topic(topic)#查找topic
        #如果对topic进行处理
        if now_topic_info is not None:
            self.a+=1
            print(self.a,"匹配到topic:",now_topic_info)
            print("\t\t数据:",payload)   
    def handle(self,gev_obj,socket,address):
        while True:
            data = socket.recv(1024)
            if not data:
                break
            if data[0]==10:
                new_data=data[1:]
                json_val=json.loads(new_data.decode('utf-8') )
                print(json_val)
                self.add_topic(json_val['topic'],json_val['info'])
                socket.sendall(new_data)
    def run_server(self):
        print("start server")
        # 创建 MQTT 客户端实例
        self.mqtt_client = mqtt.Client()
        # 连接到 MQTT 服务器
        self.mqtt_client.connect(self.broker_address, self.broker_port)
        self.mqtt_client.on_message = self.on_message
        # 开始循环监听
        self.mqtt_client.loop_start()

        #与其他进程进行通信的socket服务（用于接收数据
        self._recv()
if __name__=="__main__":
    mqtt_class.write_pid(os.getpid())
    obj=mqtt_class(**{'broker_address': '127.0.0.1', 'broker_port': 1883, 'project_dir_path': 'e:\\my\\easy_edit\\_base_tool\\python', 'recv_port': 5555, 'process_id': None, 'temp_name': 'mqtt_test'})
    obj.run_server()